package org.objective.base;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.common.plugin.PluginInfo;
import org.common.service.ServiceRegister;
import org.objective.ddl.type.DDLInteger;
import org.objective.ddl.type.DDLString;
import org.objective.ddl.type.DLLRectagle;
import org.objective.ddl.type.DataType;
import org.objective.service.Loader;

import org.objective.service.Saver;
import org.objective.service.TMService;
import static org.utils.fp.collection.CollectionFactory.*;

/**
 *
 * @author gus
 */
class DefaultStorage implements Storage
{
    private final Loader loader;
    private final Saver saver;
    private final SequenceIDGenerator generator;
    private final Map<Long, BaseObject> objects = newHashMap();
    private final Map<Long, BaseType> basetypes = newHashMap();
    private final ServiceRegister register;
    private final Map<Long, ? extends PredefinedDataType> dataTypesHash = newHashMap(
        array(DataType.String, DataType.Integer, DataType.Spatial),
        array(new DDLString(this), new DDLInteger(this), new DLLRectagle(this)));

    public DefaultStorage(ServiceRegister register) throws Exception
    {
        this.register = register;
        this.loader = register.getService(Loader.class);
        this.saver = register.getService(Saver.class);
        this.generator = new SequenceIDGenerator();
    }

    @Override
    public Loader getLoader()
    {
        return loader;
    }

    @Override
    public <T extends BaseObject> T getObject(long id)
    {
        return (T) objects.get(id);
    }

    @Override
    public <T extends BaseObject> T[] getObjects(long[] ids)
    {
        List<DomainObject> result = new ArrayList<DomainObject>(ids.length);
        for (long id : ids)
        {
            result.add(objects.get(id));
        }
        return (T[]) result.toArray();
    }

    private void loadTypeModules(TypeModule[] typeModules) throws Exception
    {
        if (typeModules == null)
        {
            throw new IllegalArgumentException("typeModules");
        }

        for (TypeModule module : typeModules)
        {
            for (BaseType type : module.getBaseTypes(this))
            {
                type.saveData(saver);
                basetypes.put(type.getID(), type);
            }
        }
    }

    public void loadBaseObjects() throws Exception
    {
        for (long typeID : getAllTypes())
        {
            BaseType basetype = getType(typeID);
            BaseObject object = basetype.newInstance();
            object.saveData(saver);
            objects.put(object.getID(), object);
        }
    }

    private void loadBaseTypes() throws Exception
    {
        PluginInfo[] infos = register.getAviablePlugins();
        assert infos != null : "not aviable plugins";
        List<TypeModule> typeModules = newArrayList(10);
        for (PluginInfo info : infos)
        {
            if (info.isModule())
            {
                TMService tms = register.getService(TMService.class, info.getName());
                typeModules.add(tms.loadTypeModule());
            }
        }
        loadTypeModules(typeModules.toArray(new TypeModule[0]));
    }

    @Override
    public PredefinedDataType getPredefineType(long type)
    {
        return dataTypesHash.get(type);
    }

    @Override
    public DomainObject stubComponent()
    {
        return stubObject;
    }

    @Override
    public boolean isStubComponent(DomainObject object)
    {
        return stubObject.getID() == object.getID();
    }

    @Override
    public <T extends DomainObject> T createObject(Construction<T> contructor)
    {
        long oid = generator.nextID(contructor.getTypeCreation());
        DomainObject object = contructor.constructObject(oid);
        if (BaseObject.class.isAssignableFrom(object.getClass()))
        {
            objects.put(oid, (BaseObject) object);
        }
        return (T) object;
    }

    @Override
    public <T extends DomainObject> T singletonObject(long oid)
    {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public <T extends BaseType> T getType(long typeID) throws Exception
    {
        BaseType type = basetypes.get(typeID);
        if (type == null)
        {
            throw new Exception(String.format("unknown type %d", typeID));
        }
        return (T) type;
    }

    @Override
    public Long[] getAllTypes() throws Exception
    {
        return basetypes.keySet().toArray(new Long[0]);
    }

    @Override
    public Long[] getAllObjects() throws Exception
    {
        return objects.keySet().toArray(new Long[0]);
    }
    private final DomainObject stubObject = new BaseDomainObjectImpl(this);

    @Override
    public SequenceIDGenerator generator()
    {
        return generator;
    }

    @Override
    public void loadMetaData() throws Exception
    {
        loadBaseTypes();
        loadBaseObjects();
    }

    private static class BaseDomainObjectImpl extends BaseDomainObject
    {
        public BaseDomainObjectImpl(Storage storage)
        {
            super(storage, 11111L);
        }

        @Override
        public String getName()
        {
            return "stubObject";
        }

        @Override
        protected UUID getUnique()
        {
            return UUID.fromString("7A366C42-B86E-4204-AD89-6EBF59D7A6CE");
        }
    }
}